use std::mem;

use windows::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE};


#[derive(Debug)]
pub(super) struct WinHandle {
    h: HANDLE,
}

unsafe impl Send for WinHandle {}
unsafe impl Sync for WinHandle {}

impl Drop for WinHandle {
    fn drop(&mut self) {
        unsafe {
            if self.is_valid() {
                let result = windows::Win32::Foundation::CloseHandle(self.h);
                assert!(result.as_bool() || std::thread::panicking());
            }
        }
    }
}

impl Default for WinHandle {
    fn default() -> WinHandle {
        WinHandle {
            h: INVALID_HANDLE_VALUE,
        }
    }
}

impl WinHandle {
    pub(super) fn new(h: HANDLE) -> WinHandle {
        WinHandle { h: h }
    }

    pub(super) fn invalid() -> WinHandle {
        WinHandle {
            h: INVALID_HANDLE_VALUE,
        }
    }

    pub(super) fn is_valid(&self) -> bool {
        self.h != INVALID_HANDLE_VALUE
    }

    pub(super) fn as_raw(&self) -> HANDLE {
        self.h
    }

    pub(super) fn take_raw(&mut self) -> HANDLE {
        mem::replace(&mut self.h, INVALID_HANDLE_VALUE)
    }

    pub(super) fn take(&mut self) -> WinHandle {
        WinHandle::new(self.take_raw())
    }
}

#[cfg(feature = "windows-shared-memory-equality")]
use windows::Win32::{
    Foundation::BOOL,
    System::LibraryLoader::{GetProcAddress, LoadLibraryA},
};

#[cfg(feature = "windows-shared-memory-equality")]
impl PartialEq for WinHandle {
    fn eq(&self, other: &WinHandle) -> bool {
        unsafe {
            // Calling LoadLibraryA every time seems to be ok since libraries are refcounted and multiple calls won't produce multiple instances.
            let module_handle = LoadLibraryA(windows::core::PCSTR::from_raw(
                WINDOWS_APP_MODULE_NAME_CSTRING.as_ptr() as *const u8,
            ))
            .unwrap_or_else(|e| panic!("Error loading library {}. {}", WINDOWS_APP_MODULE_NAME, e));
            let proc = GetProcAddress(
                module_handle,
                PCSTR::from_raw(COMPARE_OBJECT_HANDLES_FUNCTION_NAME_CSTRING.as_ptr() as *const u8),
            )
            .unwrap_or_else(|| {
                panic!(
                    "Error calling GetProcAddress to use {}. {:?}",
                    COMPARE_OBJECT_HANDLES_FUNCTION_NAME,
                    WinError::from_win32()
                )
            });
            let compare_object_handles: unsafe extern "stdcall" fn(HANDLE, HANDLE) -> BOOL =
                std::mem::transmute(proc);
            compare_object_handles(self.h, other.h).into()
        }
    }
}